home *** CD-ROM | disk | FTP | other *** search
/ Clickx 115 / Clickx 115.iso / software / tools / windows / tails-i386-0.16.iso / live / filesystem.squashfs / usr / share / system-config-printer / troubleshoot / PrintTestPage.py < prev    next >
Encoding:
Python Source  |  2010-09-28  |  19.7 KB  |  523 lines

  1. #!/usr/bin/env python
  2.  
  3. ## Printing troubleshooter
  4.  
  5. ## Copyright (C) 2008, 2009, 2010 Red Hat, Inc.
  6. ## Authors:
  7. ##  Tim Waugh <twaugh@redhat.com>
  8.  
  9. ## This program is free software; you can redistribute it and/or modify
  10. ## it under the terms of the GNU General Public License as published by
  11. ## the Free Software Foundation; either version 2 of the License, or
  12. ## (at your option) any later version.
  13.  
  14. ## This program is distributed in the hope that it will be useful,
  15. ## but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17. ## GNU General Public License for more details.
  18.  
  19. ## You should have received a copy of the GNU General Public License
  20. ## along with this program; if not, write to the Free Software
  21. ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  22.  
  23. import cups
  24. import dbus
  25. import dbus.glib
  26. import gobject
  27. import os
  28. import pango
  29. import tempfile
  30. import time
  31. from timedops import TimedOperation, OperationCanceled
  32.  
  33. from base import *
  34.  
  35. import errordialogs
  36. errordialogs.set_gettext_function (_)
  37. from errordialogs import *
  38.  
  39. DBUS_PATH="/com/redhat/PrinterSpooler"
  40. DBUS_IFACE="com.redhat.PrinterSpooler"
  41. class PrintTestPage(Question):
  42.     STATE = { cups.IPP_JOB_PENDING: _("Pending"),
  43.               cups.IPP_JOB_HELD: _("Held"),
  44.               cups.IPP_JOB_PROCESSING: _("Processing"),
  45.               cups.IPP_JOB_STOPPED: _("Stopped"),
  46.               cups.IPP_JOB_CANCELED: _("Canceled"),
  47.               cups.IPP_JOB_ABORTED: _("Aborted"),
  48.               cups.IPP_JOB_COMPLETED: _("Completed") }
  49.  
  50.     def __init__ (self, troubleshooter):
  51.         Question.__init__ (self, troubleshooter, "Print test page")
  52.         page = gtk.VBox ()
  53.         page.set_spacing (12)
  54.         page.set_border_width (12)
  55.  
  56.         label = gtk.Label ()
  57.         label.set_alignment (0, 0)
  58.         label.set_use_markup (True)
  59.         label.set_line_wrap (True)
  60.         page.pack_start (label, False, False, 0)
  61.         self.main_label = label
  62.         self.main_label_text = ('<span weight="bold" size="larger">' +
  63.                                 _("Test Page") + '</span>\n\n' +
  64.                                 _("Now print a test page.  If you are having "
  65.                                   "problems printing a specific document, "
  66.                                   "print that document now and mark the print "
  67.                                   "job below."))
  68.  
  69.         hbox = gtk.HButtonBox ()
  70.         hbox.set_border_width (0)
  71.         hbox.set_spacing (3)
  72.         hbox.set_layout (gtk.BUTTONBOX_START)
  73.         self.print_button = gtk.Button (_("Print Test Page"))
  74.         hbox.pack_start (self.print_button, False, False, 0)
  75.  
  76.         self.cancel_button = gtk.Button (_("Cancel All Jobs"))
  77.         hbox.pack_start (self.cancel_button, False, False, 0)
  78.         page.pack_start (hbox, False, False, 0)
  79.  
  80.         tv = gtk.TreeView ()
  81.         test_cell = gtk.CellRendererToggle ()
  82.         test = gtk.TreeViewColumn (_("Test"), test_cell, active=0)
  83.         job = gtk.TreeViewColumn (_("Job"), gtk.CellRendererText (), text=1)
  84.         printer_cell = gtk.CellRendererText ()
  85.         printer = gtk.TreeViewColumn (_("Printer"), printer_cell, text=2)
  86.         name_cell = gtk.CellRendererText ()
  87.         name = gtk.TreeViewColumn (_("Document"), name_cell, text=3)
  88.         status = gtk.TreeViewColumn (_("Status"), gtk.CellRendererText (),
  89.                                      text=4)
  90.         test_cell.set_radio (False)
  91.         self.test_cell = test_cell
  92.         printer.set_resizable (True)
  93.         printer_cell.set_property ("ellipsize", pango.ELLIPSIZE_END)
  94.         printer_cell.set_property ("width-chars", 20)
  95.         name.set_resizable (True)
  96.         name_cell.set_property ("ellipsize", pango.ELLIPSIZE_END)
  97.         name_cell.set_property ("width-chars", 20)
  98.         status.set_resizable (True)
  99.         tv.append_column (test)
  100.         tv.append_column (job)
  101.         tv.append_column (printer)
  102.         tv.append_column (name)
  103.         tv.append_column (status)
  104.         tv.set_rules_hint (True)
  105.         sw = gtk.ScrolledWindow ()
  106.         sw.set_policy (gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
  107.         sw.set_shadow_type (gtk.SHADOW_IN)
  108.         sw.add (tv)
  109.         self.treeview = tv
  110.         page.pack_start (sw)
  111.  
  112.         label = gtk.Label (_("Did the marked print jobs print correctly?"))
  113.         label.set_line_wrap (True)
  114.         label.set_alignment (0, 0)
  115.         page.pack_start (label, False, False, 0)
  116.  
  117.         vbox = gtk.VBox ()
  118.         vbox.set_spacing (6)
  119.         self.yes = gtk.RadioButton (label=_("Yes"))
  120.         no = gtk.RadioButton (label=_("No"))
  121.         no.set_group (self.yes)
  122.         vbox.pack_start (self.yes, False, False, 0)
  123.         vbox.pack_start (no, False, False, 0)
  124.         page.pack_start (vbox, False, False, 0)
  125.         self.persistent_answers = {}
  126.         troubleshooter.new_page (page, self)
  127.  
  128.     def display (self):
  129.         answers = self.troubleshooter.answers
  130.         if not answers.has_key ('cups_queue'):
  131.             return False
  132.  
  133.         parent = self.troubleshooter.get_window ()
  134.         self.authconn = answers['_authenticated_connection']
  135.         mediatype = None
  136.         defaults = answers.get ('cups_printer_ppd_defaults', {})
  137.         for opts in defaults.values ():
  138.             for opt, value in opts.iteritems ():
  139.                 if opt == "MediaType":
  140.                     mediatype = value
  141.                     break
  142.  
  143.         if mediatype != None:
  144.             mediatype_string = '\n\n' + (_("Remember to load paper of type "
  145.                                            "'%s' into the printer first.") %
  146.                                          mediatype)
  147.         else:
  148.             mediatype_string = ""
  149.  
  150.         label_text = self.main_label_text + mediatype_string
  151.         self.main_label.set_markup (label_text)
  152.  
  153.         model = gtk.ListStore (gobject.TYPE_BOOLEAN,
  154.                                gobject.TYPE_INT,
  155.                                gobject.TYPE_STRING,
  156.                                gobject.TYPE_STRING,
  157.                                gobject.TYPE_STRING)
  158.         self.treeview.set_model (model)
  159.         self.job_to_iter = {}
  160.  
  161.         test_jobs = self.persistent_answers.get ('test_page_job_id', [])
  162.         def get_jobs ():
  163.             c = self.authconn
  164.             jobs_dict = c.getJobs (which_jobs='not-completed',
  165.                                    my_jobs=False)
  166.             completed_jobs_dict = c.getJobs (which_jobs='completed')
  167.             return (jobs_dict, completed_jobs_dict)
  168.  
  169.         self.op = TimedOperation (get_jobs, parent=parent)
  170.         try:
  171.             (jobs_dict, completed_jobs_dict) = self.op.run ()
  172.         except (OperationCanceled, cups.IPPError):
  173.             return False
  174.  
  175.         # We want to display the jobs in the queue for this printer...
  176.         try:
  177.             queue_uri_ending = "/" + self.troubleshooter.answers['cups_queue']
  178.             jobs_on_this_printer = filter (lambda x:
  179.                                                jobs_dict[x]['job-printer-uri'].\
  180.                                                endswith (queue_uri_ending),
  181.                                            jobs_dict.keys ())
  182.         except:
  183.             jobs_on_this_printer = []
  184.  
  185.         # ...as well as any other jobs we've previous submitted as test pages.
  186.         jobs = list (set(test_jobs).union (set (jobs_on_this_printer)))
  187.  
  188.         completed_jobs_dict = None
  189.         for job in jobs:
  190.             try:
  191.                 j = jobs_dict[job]
  192.             except KeyError:
  193.                 try:
  194.                     j = completed_jobs_dict[job]
  195.                 except KeyError:
  196.                     continue
  197.  
  198.             iter = model.append (None)
  199.             self.job_to_iter[job] = iter
  200.             model.set_value (iter, 0, job in test_jobs)
  201.             model.set_value (iter, 1, job)
  202.             self.update_job (job, j)
  203.  
  204.         return True
  205.  
  206.     def connect_signals (self, handler):
  207.         self.print_sigid = self.print_button.connect ("clicked",
  208.                                                       self.print_clicked)
  209.         self.cancel_sigid = self.cancel_button.connect ("clicked",
  210.                                                         self.cancel_clicked)
  211.         self.test_sigid = self.test_cell.connect ('toggled',
  212.                                                   self.test_toggled)
  213.  
  214.         def create_subscription ():
  215.             c = self.authconn
  216.             sub_id = c.createSubscription ("/",
  217.                                            events=["job-created",
  218.                                                    "job-completed",
  219.                                                    "job-stopped",
  220.                                                    "job-progress",
  221.                                                    "job-state-changed"])
  222.             return sub_id
  223.  
  224.         parent = self.troubleshooter.get_window ()
  225.         self.op = TimedOperation (create_subscription, parent=parent)
  226.         try:
  227.             self.sub_id = self.op.run ()
  228.         except (OperationCanceled, cups.IPPError):
  229.             pass
  230.  
  231.         try:
  232.             bus = dbus.SystemBus ()
  233.         except:
  234.             bus = None
  235.  
  236.         self.bus = bus
  237.         if bus:
  238.             bus.add_signal_receiver (self.handle_dbus_signal,
  239.                                      path=DBUS_PATH,
  240.                                      dbus_interface=DBUS_IFACE)
  241.  
  242.         self.timer = gobject.timeout_add_seconds (1, self.update_jobs_list)
  243.  
  244.     def disconnect_signals (self):
  245.         if self.bus:
  246.             self.bus.remove_signal_receiver (self.handle_dbus_signal,
  247.                                              path=DBUS_PATH,
  248.                                              dbus_interface=DBUS_IFACE)
  249.                                              
  250.         self.print_button.disconnect (self.print_sigid)
  251.         self.cancel_button.disconnect (self.cancel_sigid)
  252.         self.test_cell.disconnect (self.test_sigid)
  253.  
  254.         def cancel_subscription (sub_id):
  255.             c = self.authconn
  256.             c.cancelSubscription (sub_id)
  257.  
  258.         parent = self.troubleshooter.get_window ()
  259.         self.op = TimedOperation (cancel_subscription,
  260.                                   (self.sub_id,),
  261.                                   parent=parent)
  262.         try:
  263.             self.op.run ()
  264.         except (OperationCanceled, cups.IPPError):
  265.             pass
  266.  
  267.         try:
  268.             del self.sub_seq
  269.         except:
  270.             pass
  271.  
  272.         gobject.source_remove (self.timer)
  273.  
  274.     def collect_answer (self):
  275.         if not self.displayed:
  276.             return {}
  277.  
  278.         self.answers = self.persistent_answers.copy ()
  279.         parent = self.troubleshooter.get_window ()
  280.         success = self.yes.get_active ()
  281.         self.answers['test_page_successful'] = success
  282.  
  283.         class collect_jobs:
  284.             def __init__ (self, model):
  285.                 self.jobs = []
  286.                 model.foreach (self.each, None)
  287.  
  288.             def each (self, model, path, iter, user_data):
  289.                 self.jobs.append (model.get (iter, 0, 1, 2, 3, 4))
  290.  
  291.         model = self.treeview.get_model ()
  292.         jobs = collect_jobs (model).jobs
  293.         def collect_attributes (jobs):
  294.             job_attrs = None
  295.             c = self.authconn
  296.             with_attrs = []
  297.             for (test, jobid, printer, doc, status) in jobs:
  298.                 attrs = None
  299.                 if test:
  300.                     try:
  301.                         attrs = c.getJobAttributes (jobid)
  302.                     except AttributeError:
  303.                         # getJobAttributes was introduced in pycups 1.9.35.
  304.                         if job_attrs == None:
  305.                             job_attrs = c.getJobs (which_jobs='all')
  306.  
  307.                         attrs = self.job_attrs[jobid]
  308.  
  309.                 with_attrs.append ((test, jobid, printer, doc, status, attrs))
  310.  
  311.             return with_attrs
  312.  
  313.         self.op = TimedOperation (collect_attributes,
  314.                                   (jobs,),
  315.                                   parent=parent)
  316.         try:
  317.             with_attrs = self.op.run ()
  318.             self.answers['test_page_job_status'] = with_attrs
  319.         except (OperationCanceled, cups.IPPError):
  320.             pass
  321.  
  322.         return self.answers
  323.  
  324.     def cancel_operation (self):
  325.         self.op.cancel ()
  326.  
  327.         # Abandon the CUPS connection and make another.
  328.         answers = self.troubleshooter.answers
  329.         factory = answers['_authenticated_connection_factory']
  330.         self.authconn = factory.get_connection ()
  331.         self.answers['_authenticated_connection'] = self.authconn
  332.  
  333.     def handle_dbus_signal (self, *args):
  334.         debugprint ("D-Bus signal caught: updating jobs list soon")
  335.         gobject.source_remove (self.timer)
  336.         self.timer = gobject.timeout_add (200, self.update_jobs_list)
  337.  
  338.     def update_job (self, jobid, job_dict):
  339.         iter = self.job_to_iter[jobid]
  340.         model = self.treeview.get_model ()
  341.         try:
  342.             printer_name = job_dict['printer-name']
  343.         except KeyError:
  344.             try:
  345.                 uri = job_dict['job-printer-uri']
  346.                 r = uri.rfind ('/')
  347.                 printer_name = uri[r + 1:]
  348.             except KeyError:
  349.                 printer_name = None
  350.  
  351.         if printer_name != None:
  352.             model.set_value (iter, 2, printer_name)
  353.  
  354.         model.set_value (iter, 3, job_dict['job-name'])
  355.         model.set_value (iter, 4, self.STATE[job_dict['job-state']])
  356.  
  357.     def print_clicked (self, widget):
  358.         now = time.time ()
  359.         tt = time.localtime (now)
  360.         when = time.strftime ("%d/%b/%Y:%T %z", tt)
  361.         self.persistent_answers['test_page_attempted'] = when
  362.         answers = self.troubleshooter.answers
  363.         parent = self.troubleshooter.get_window ()
  364.  
  365.         def print_test_page (*args, **kwargs):
  366.             factory = answers['_authenticated_connection_factory']
  367.             c = factory.get_connection ()
  368.             return c.printTestPage (*args, **kwargs)
  369.  
  370.         tmpfname = None
  371.         mimetypes = [None, 'text/plain']
  372.         for mimetype in mimetypes:
  373.             try:
  374.                 if mimetype == None:
  375.                     # Default test page.
  376.                     self.op = TimedOperation (print_test_page,
  377.                                               (answers['cups_queue'],),
  378.                                               parent=parent)
  379.                     jobid = self.op.run ()
  380.                 elif mimetype == 'text/plain':
  381.                     (tmpfd, tmpfname) = tempfile.mkstemp ()
  382.                     os.write (tmpfd, "This is a test page.\n")
  383.                     os.close (tmpfd)
  384.                     self.op = TimedOperation (print_test_page,
  385.                                               (answers['cups_queue'],),
  386.                                               kwargs={'file': tmpfname,
  387.                                                       'format': mimetype},
  388.                                               parent=parent)
  389.                     jobid = self.op.run ()
  390.                     try:
  391.                         os.unlink (tmpfname)
  392.                     except OSError:
  393.                         pass
  394.  
  395.                     tmpfname = None
  396.  
  397.                 jobs = self.persistent_answers.get ('test_page_job_id', [])
  398.                 jobs.append (jobid)
  399.                 self.persistent_answers['test_page_job_id'] = jobs
  400.                 break
  401.             except OperationCanceled:
  402.                 self.persistent_answers['test_page_submit_failure'] = 'cancel'
  403.                 break
  404.             except RuntimeError:
  405.                 self.persistent_answers['test_page_submit_failure'] = 'connect'
  406.                 break
  407.             except cups.IPPError, (e, s):
  408.                 if (e == cups.IPP_DOCUMENT_FORMAT and
  409.                     mimetypes.index (mimetype) < (len (mimetypes) - 1)):
  410.                     # Try next format.
  411.                     if tmpfname != None:
  412.                         os.unlink (tmpfname)
  413.                         tmpfname = None
  414.                     continue
  415.  
  416.                 self.persistent_answers['test_page_submit_failure'] = (e, s)
  417.                 show_error_dialog (_("Error submitting test page"),
  418.                                    _("There was an error during the CUPS "
  419.                                      "operation: '%s'.") % s,
  420.                                    self.troubleshooter.get_window ())
  421.                 break
  422.  
  423.     def cancel_clicked (self, widget):
  424.         self.persistent_answers['test_page_jobs_cancelled'] = True
  425.         jobids = []
  426.         for jobid, iter in self.job_to_iter.iteritems ():
  427.             jobids.append (jobid)
  428.  
  429.         def cancel_jobs (jobids):
  430.             c = self.authconn
  431.             for jobid in jobids:
  432.                 try:
  433.                     c.cancelJob (jobid)
  434.                 except cups.IPPError, (e, s):
  435.                     if e != cups.IPP_NOT_POSSIBLE:
  436.                         self.persistent_answers['test_page_cancel_failure'] = (e, s)
  437.  
  438.         self.op = TimedOperation (cancel_jobs,
  439.                                   (jobids,),
  440.                                   parent=self.troubleshooter.get_window ())
  441.         try:
  442.             self.op.run ()
  443.         except (OperationCanceled, cups.IPPError):
  444.             pass
  445.  
  446.     def test_toggled (self, cell, path):
  447.         model = self.treeview.get_model ()
  448.         iter = model.get_iter (path)
  449.         active = model.get_value (iter, 0)
  450.         model.set_value (iter, 0, not active)
  451.  
  452.     def update_jobs_list (self):
  453.         def get_notifications (self):
  454.             c = self.authconn
  455.             try:
  456.                 notifications = c.getNotifications ([self.sub_id],
  457.                                                     [self.sub_seq + 1])
  458.             except AttributeError:
  459.                 notifications = c.getNotifications ([self.sub_id])
  460.  
  461.             return notifications
  462.  
  463.         # Enter the GDK lock.  We need to do this because we were
  464.         # called from a timeout.
  465.         gtk.gdk.threads_enter ()
  466.  
  467.         parent = self.troubleshooter.get_window ()
  468.         self.op = TimedOperation (get_notifications,
  469.                                   (self,),
  470.                                   parent=parent)
  471.         try:
  472.             notifications = self.op.run ()
  473.         except (OperationCanceled, cups.IPPError):
  474.             gtk.gdk.threads_leave ()
  475.             return True
  476.  
  477.         answers = self.troubleshooter.answers
  478.         model = self.treeview.get_model ()
  479.         queue = answers['cups_queue']
  480.         test_jobs = self.persistent_answers.get('test_page_job_id', [])
  481.         for event in notifications['events']:
  482.             seq = event['notify-sequence-number']
  483.             try:
  484.                 if seq <= self.sub_seq:
  485.                     # Work around a bug in pycups < 1.9.34
  486.                     continue
  487.             except AttributeError:
  488.                 pass
  489.             self.sub_seq = seq
  490.             job = event['notify-job-id']
  491.  
  492.             nse = event['notify-subscribed-event']
  493.             if nse == 'job-created':
  494.                 if (job in test_jobs or
  495.                     event['printer-name'] == queue):
  496.                     iter = model.append (None)
  497.                     self.job_to_iter[job] = iter
  498.                     model.set_value (iter, 0, True)
  499.                     model.set_value (iter, 1, job)
  500.                 else:
  501.                     continue
  502.             elif not self.job_to_iter.has_key (job):
  503.                 continue
  504.  
  505.             if (job in test_jobs and
  506.                 nse in ["job-stopped", "job-completed"]):
  507.                 comp = self.persistent_answers.get ('test_page_completions', [])
  508.                 comp.append ((job, event['notify-text']))
  509.                 self.persistent_answers['test_page_completions'] = comp
  510.  
  511.             self.update_job (job, event)
  512.  
  513.         # Update again when we're told to. (But we might update sooner if
  514.         # there is a D-Bus signal.)
  515.         gobject.source_remove (self.timer)
  516.         self.timer = gobject.timeout_add_seconds (
  517.             notifications['notify-get-interval'],
  518.             self.update_jobs_list)
  519.         debugprint ("Update again in %ds" %
  520.                     notifications['notify-get-interval'])
  521.         gtk.gdk.threads_leave ()
  522.         return False
  523.